package com.wtpaint.lib.bus;

import com.wtpaint.lib.bus.annotation.ZSubscribe;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 模仿EventBus的方式进行消息传递 通用场景fragment与fragment之间通信
 */
public class ZBus {
    //由于用到反射还有各种循环等会影响性能，故用Map缓存已经存入
    //存储订阅方法列表与类对象的映射表
    private static final Map<Class<?>, List<ZSubscriberMethod>> METHOD_CACHE = new HashMap<>();

    //存储所有的订阅者的订阅集合
    private static final Map<String, List<ZSubscription>> SUBSCRIBES = new HashMap<>();

    //类对象与订阅标签的集合
    private static final Map<Class<?>, List<String>> REGISTERS = new HashMap<>();

    private static ZBus mInstance;

    public static ZBus getDefault() {
        if (null == mInstance) {
            synchronized (ZBus.class) {
                if (null == mInstance) {
                    mInstance = new ZBus();
                }
            }
        }
        return mInstance;
    }

    //注册订阅者  本质：将标签与订阅者关联
    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<ZSubscriberMethod> subscriberMethods = findSubscribe(subscriberClass);
        List<String> labels = REGISTERS.get(subscriberClass);
        if (null == labels) {
            labels = new ArrayList<>();
        }
        for (ZSubscriberMethod zSubscriberMethod : subscriberMethods) {
            String label = zSubscriberMethod.getLabel();
            if (!labels.contains(label)) {
                labels.add(label);
            }
            //根据标签获取到订阅者的信息集合
            List<ZSubscription> subscriptions = SUBSCRIBES.get(label);
            if (null == subscriptions) {
                subscriptions = new ArrayList<>();
                SUBSCRIBES.put(label, subscriptions);
            }

            ZSubscription subscription = new ZSubscription(subscriber, zSubscriberMethod);
            subscriptions.add(subscription);
        }
        REGISTERS.put(subscriberClass, labels);
    }

    //反注册
    public void unregister(Object subscriber) {
        List<String> labels = REGISTERS.remove(subscriber.getClass());
        if (null != labels) {
            for (String label : labels) {
                //根据标签查找记录
                List<ZSubscription> subscriptions = SUBSCRIBES.get(label);
                if (null != subscriptions) {
                    Iterator<ZSubscription> iterator = subscriptions.iterator();
                    while (iterator.hasNext()) {
                        ZSubscription subscription = iterator.next();
                        //对象是同一个 则删除
                        if (subscription.getSubscriber() == subscriber) {
                            iterator.remove();
                        }
                    }
                }
            }
        }
    }

    //发送消息：标签与内容
    public void post(String label, Object... params) {
        List<ZSubscription> subscriptions = SUBSCRIBES.get(label);
        for (ZSubscription subscription : subscriptions) {
            ZSubscriberMethod subscriberMethod = subscription.getSubscriberMethod();
            Class<?>[] parameters = subscriberMethod.getParameters();
            Object[] realParams = new Object[parameters.length];
            if (null != params) {
                for (int i = 0; i < parameters.length; i++) {
                    if (i < parameters.length && parameters[i].isInstance(params[i])) {
                        realParams[i] = params[i];
                    } else {
                        realParams[i] = null;
                    }
                }
            }
            try {
                subscriberMethod.getMethod().invoke(subscription.getSubscriber(), realParams);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    //从缓存表中寻找订阅者方法
    private List<ZSubscriberMethod> findSubscribe(Class<?> subscriberClass) {

        List<ZSubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (null == subscriberMethods) {
            subscriberMethods = new ArrayList<>();
            Method[] declaredMethods = subscriberClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                ZSubscribe annotation = method.getAnnotation(ZSubscribe.class);
                if (null != annotation) {
                    String[] values = annotation.value();
                    Class<?>[] parameters = method.getParameterTypes();
                    for (String value : values) {
                        method.setAccessible(true);
                        ZSubscriberMethod subscriberMethod = new ZSubscriberMethod(value, method, parameters);
                        subscriberMethods.add(subscriberMethod);
                    }
                }
            }
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
        }
        return subscriberMethods;
    }

}
